李守中

OpenVPN 配置

Table of Contents

1. 配置目标

现有:

  • 带公网 IP 的 VPS 一台
  • 地点 A 网段 a 192.168.1.0/24 无公网 IP
  • 地点 B 网段 b 192.168.2.0/24 无公网 IP

在 VPS 上建立 OpenVPN 服务端,虚拟网段 192.168.0.0/24。在网段 a 和 b 中各有一台 RouterOS 作为客户端。配置之后子网 a 和子网 b 中的设备可以互相通信。

以下操作以 Debian 系发行版为例。

2. 在 OpenVPN 服务端上打开端口转发

打开内核的数据包转发功能:

# enable ip forward

# enable temporarily
$ echo "1" > /proc/sys/net/ipv4/ip_forward

# enable permantely
$ echo "net.ipv4.ip_forward = 1" >> /etc/sysctl.conf
$ sysctl -p /etc/sysctl.conf

3. 安装 OpenVPN 和 Easy-RSA

sudo apt install openvpn easy-rsa 安装相关的包。

切换到非 root 用户,执行 mkdir ~/easy-rsa && ln -s /usr/share/easy-rsa/* ~/easy-rsa/ 创建 Easy-RSA 的软链接。

注: 有些教程会写直接复制 /usr/share/easy-rsa 到用户家目录下,而一旦 Easy-RSA 更新,则需要重新复制相关文件。使用软链接可以避免需要手动复制相关文件的问题。

chmod 700 ~/easy-rsa 给 Easy-RSA 目录正确的权限。

4. 构建 CA

cd ~/easy-rsa 进入 easy-rsa 目录。

cp vars.example vars 从 vars 模板复制出一个 vars 文件,然后按需求自行改动 ( 当然也可以不改 )。

./easyrsa init-pki 初始化 PKI。

注: PKI 系统由公钥密码技术 ( Public Key Cryptography )、证书认证机构 ( CA )、注册机构 ( RA )、数字证书 ( Digital Certificate ) 和相应的 PKI 存储库组成。

./easyrsa build-ca nopass 构建 CA。这个操作将生成构建 SSL 证书需要的 <ca>.crt 公钥和 <ca>.key 私钥。使用 nopass 可以做到用户和 CA 交互时不需要密码。

在输出中,系统会要求确认 CA 的 Common Name ,可以自行修改,也可以不改。这里直接按 Enter 跳过。像这样的:

...
Common Name (eg: your user, host, or server name) [Easy-RSA CA]:

CA creation complete and you may now import and sign cert requests.
Your new CA certificate file for publishing is at:
/home/lsz/easy-rsa/pki/ca.crt

从输出可以看到 ca.cert 就存储在 ~/easy-rsa/pki 目录下。而 ca.key 则存储在 ~/easy-rsa/pki/private 目录下。

5. 给服务端创建证书、密钥和加密文件

上一节的操作已经生成了一个 CA。现在需要在服务器上生成一个私钥和证书签发请求,然后让 CA 根据请求签发证书。

cd ~/easy-rsa 进入 easy-rsa 目录。

./easyrsa gen-req openvpn-server nopass 这个命令会生成一个名为 openvpn-server 的私钥文件与用于请求证书的 req 文件。输出的关键部分类似下面这样:

...
Common Name (eg: your user, host, or server name) [openvpn-server]:

Keypair and certificate request completed. Your files are:
req: /home/lsz/easy-rsa/pki/reqs/openvpn-server.req
key: /home/lsz/easy-rsa/pki/private/openvpn-server.key

从输出可以看到,私钥文件为 /home/lsz/easy-rsa/pki/private/openvpn-server.key。

./easyrsa sign-req server openvpn-server 这个命令根据上面生成的 .req 文件签发证书。执行过程中需要输入 yes 。输出类似这样的:

Using SSL: openssl OpenSSL 1.1.1k  25 Mar 2021 (Library: OpenSSL 1.1.1n  15 Mar 2022)


You are about to sign the following certificate.
Please check over the details shown below for accuracy. Note that this request
has not been cryptographically verified. Please be sure it came from a trusted
source or that you have verified the request checksum with the sender.

Request subject, to be signed as a server certificate for 825 days:

subject=
    commonName                = openvpn-server


Type the word 'yes' to continue, or any other input to abort.
  Confirm request details: yes
Using configuration from /home/lsz/easy-rsa/pki/easy-rsa-4028.5inmU9/tmp.IZAkPh
Check that the request matches the signature
Signature ok
The Subject's Distinguished Name is as follows
commonName            :ASN.1 12:'openvpn-server'
Certificate is to be certified until Aug 31 08:02:09 2024 GMT (825 days)

Write out database with 1 new entries
Data Base Updated

Certificate created at: /home/lsz/easy-rsa/pki/issued/openvpn-server.crt

从最后一行输出可以看到,签名好的证书文件为 /home/lsz/easy-rsa/pki/issued/openvpn-server.crt。

这样 openvpn 服务端需要的证书文件和私钥文件就做好了。

然后运行 ./easyrsa gen-dh 生成用于 SSL/TLS 连接的 Diffie-Hellman 文件。

最后把密钥与证书等文件复制到 /etc/openvpn/server。

sudo cp ~/easy-rsa/pki/ca.crt /etc/openvpn/server
sudo cp ~/easy-rsa/pki/dh.pem /etc/openvpn/server
sudo cp ~/easy-rsa/pki/private/openvpn-server.key /etc/openvpn/server
sudo cp ~/easy-rsa/pki/issued/openvpn-server.crt /etc/openvpn/server

6. 给客户端创建证书和密钥文件

用同样的方法给客户端创建一个证书和密钥文件:

  • ./easyrsa gen-req openvpn-client-a nopass 创建 .req 证书签发请求文件和密钥文件。
  • ./easyrsa sign-req client openvpn-client-a 签发证书。

最终得到客户端的密钥文件 /home/lsz/easy-rsa/pki/private/openvpn-client-a.key 与客户端的证书文件 /home/lsz/easy-rsa/pki/issued/openvpn-client-a.crt。

按需创建一个或者多个。

7. OpenVPN 服务端配置

cp /usr/share/doc/openvpn/examples/sample-config-files/server.conf /etc/openvpn/server/myserver.conf 从服务端配置模板复制一个文件到服务端配置目录,改好配置后:

  • 让 OpenVPN 开机自启: sudo systemctl enable openvpn-server@myserver
  • 重新启动 OpenVPN: sudo systemctl restart openvpn-server@myserver

这里记录一个自用的配置。

/etc/openvpn/server/ccd/openvpn-client-a 中,允许访问客户端后的网络,并配置静态 IP:

iroute 192.168.1.0 255.255.255.0
ifconfig-push 192.168.0.5 192.168.0.6

/etc/openvpn/server/ccd/openvpn-client-b 中,允许访问客户端后的网络,并配置静态 IP:

iroute 192.168.2.0 255.255.255.0
ifconfig-push 192.168.0.9 192.168.0.10

/etc/openvpn/server/myserver.conf 中:

#################################################
# Sample OpenVPN 2.0 config file for            #
# multi-client server.                          #
#                                               #
# This file is for the server side              #
# of a many-clients <-> one-server              #
# OpenVPN configuration.                        #
#                                               #
# OpenVPN also supports                         #
# single-machine <-> single-machine             #
# configurations (See the Examples page         #
# on the web site for more info).               #
#                                               #
# This config should work on Windows            #
# or Linux/BSD systems.  Remember on            #
# Windows to quote pathnames and use            #
# double backslashes, e.g.:                     #
# "C:\\Program Files\\OpenVPN\\config\\foo.key" #
#                                               #
# Comments are preceded with '#' or ';'         #
#################################################

# Which local IP address should OpenVPN
# listen on? (optional)
# 默认监听 0.0.0.0
;local a.b.c.d

# Which TCP/UDP port should OpenVPN listen on?
# If you want to run multiple OpenVPN instances
# on the same machine, use a different port
# number for each one.  You will need to
# open up this port on your firewall.
port 39393

# TCP or UDP server?
# udp 被 QoS 得很严重的话,可以用 tcp
#proto tcp
proto udp

# "dev tun" will create a routed IP tunnel,
# "dev tap" will create an ethernet tunnel.
# Use "dev tap0" if you are ethernet bridging
# and have precreated a tap0 virtual interface
# and bridged it with your ethernet interface.
# If you want to control access policies
# over the VPN, you must create firewall
# rules for the the TUN/TAP interface.
# On non-Windows systems, you can give
# an explicit unit number, such as tun0.
# On Windows, use "dev-node" for this.
# On most systems, the VPN will not function
# unless you partially or fully disable
# the firewall for the TUN/TAP interface.
# 做组网的话 tun 就够用了,有些系统对 tap 的支持不好
;dev tap
dev tun0

# Windows needs the TAP-Win32 adapter name
# from the Network Connections panel if you
# have more than one.  On XP SP2 or higher,
# you may need to selectively disable the
# Windows firewall for the TAP adapter.
# Non-Windows systems usually don't need this.
;dev-node MyTap

# SSL/TLS root certificate (ca), certificate
# (cert), and private key (key).  Each client
# and the server must have their own cert and
# key file.  The server and all clients will
# use the same ca file.
#
# See the "easy-rsa" directory for a series
# of scripts for generating RSA certificates
# and private keys.  Remember to use
# a unique Common Name for the server
# and each of the client certificates.
#
# Any X509 key management system can be used.
# OpenVPN can also use a PKCS #12 formatted key file
# (see "pkcs12" directive in man page).
ca /etc/openvpn/server/ca.crt
cert /etc/openvpn/server/openvpn-server.crt
key /etc/openvpn/server/openvpn-server.key  # This file should be kept secret

# Diffie hellman parameters.
# Generate your own with:
#   openssl dhparam -out dh2048.pem 2048
dh /etc/openvpn/server/dh.pem

# Network topology
# Should be subnet (addressing via IP)
# unless Windows clients v2.0.9 and lower have to
# be supported (then net30, i.e. a /30 per client)
# Defaults to net30 (not recommended)
;topology subnet

# Configure server mode and supply a VPN subnet
# for OpenVPN to draw client addresses from.
# The server will take 10.8.0.1 for itself,
# the rest will be made available to clients.
# Each client will be able to reach the server
# on 10.8.0.1. Comment this line out if you are
# ethernet bridging. See the man page for more info.
# 配置 openvpn 所在的虚拟网段
server 192.168.0.0 255.255.255.0

# Maintain a record of client <-> virtual IP address
# associations in this file.  If OpenVPN goes down or
# is restarted, reconnecting clients can be assigned
# the same virtual IP address from the pool that was
# previously assigned.
ifconfig-pool-persist /var/log/openvpn/ipp.txt

# Configure server mode for ethernet bridging.
# You must first use your OS's bridging capability
# to bridge the TAP interface with the ethernet
# NIC interface.  Then you must manually set the
# IP/netmask on the bridge interface, here we
# assume 10.8.0.4/255.255.255.0.  Finally we
# must set aside an IP range in this subnet
# (start=10.8.0.50 end=10.8.0.100) to allocate
# to connecting clients.  Leave this line commented
# out unless you are ethernet bridging.
;server-bridge 10.8.0.4 255.255.255.0 10.8.0.50 10.8.0.100

# Configure server mode for ethernet bridging
# using a DHCP-proxy, where clients talk
# to the OpenVPN server-side DHCP server
# to receive their IP address allocation
# and DNS server addresses.  You must first use
# your OS's bridging capability to bridge the TAP
# interface with the ethernet NIC interface.
# Note: this mode only works on clients (such as
# Windows), where the client-side TAP adapter is
# bound to a DHCP client.
;server-bridge

# Push routes to the client to allow it
# to reach other private subnets behind
# the server.  Remember that these
# private subnets will also need
# to know to route the OpenVPN client
# address pool (10.8.0.0/255.255.255.0)
# back to the OpenVPN server.
# 向各个客户端推送路由表,不操作本地路由表
push "route 192.168.1.0 255.255.255.0"
push "route 192.168.2.0 255.255.255.0"

# To assign specific IP addresses to specific
# clients or if a connecting client has a private
# subnet behind it that should also have VPN access,
# use the subdirectory "ccd" for client-specific
# configuration files (see man page for more info).

# EXAMPLE: Suppose the client
# having the certificate common name "Thelonious"
# also has a small subnet behind his connecting
# machine, such as 192.168.40.128/255.255.255.248.
# First, uncomment out these lines:
;client-config-dir ccd
;route 192.168.40.128 255.255.255.248
# Then create a file ccd/Thelonious with this line:
#   iroute 192.168.40.128 255.255.255.248
# This will allow Thelonious' private subnet to
# access the VPN.  This example will only work
# if you are routing, not bridging, i.e. you are
# using "dev tun" and "server" directives.
# 这里的配置是为了各个客户端后的子网可以访问 vpn
client-config-dir ccd
# 添加本地路由表,这里的配置不向客户端推送
route 192.168.1.0 255.255.255.0
route 192.168.2.0 255.255.255.0

# EXAMPLE: Suppose you want to give
# Thelonious a fixed VPN IP address of 10.9.0.1.
# First uncomment out these lines:
;client-config-dir ccd
;route 10.9.0.0 255.255.255.252
# Then add this line to ccd/Thelonious:
#   ifconfig-push 10.9.0.1 10.9.0.2

# Suppose that you want to enable different
# firewall access policies for different groups
# of clients.  There are two methods:
# (1) Run multiple OpenVPN daemons, one for each
#     group, and firewall the TUN/TAP interface
#     for each group/daemon appropriately.
# (2) (Advanced) Create a script to dynamically
#     modify the firewall in response to access
#     from different clients.  See man
#     page for more info on learn-address script.
;learn-address ./script

# If enabled, this directive will configure
# all clients to redirect their default
# network gateway through the VPN, causing
# all IP traffic such as web browsing and
# and DNS lookups to go through the VPN
# (The OpenVPN server machine may need to NAT
# or bridge the TUN/TAP interface to the internet
# in order for this to work properly).
;push "redirect-gateway def1 bypass-dhcp"

# Certain Windows-specific network settings
# can be pushed to clients, such as DNS
# or WINS server addresses.  CAVEAT:
# http://openvpn.net/faq.html#dhcpcaveats
# The addresses below refer to the public
# DNS servers provided by opendns.com.
;push "dhcp-option DNS 208.67.222.222"
;push "dhcp-option DNS 208.67.220.220"

# Uncomment this directive to allow different
# clients to be able to "see" each other.
# By default, clients will only see the server.
# To force clients to only see the server, you
# will also need to appropriately firewall the
# server's TUN/TAP interface.
# 让客户端可以互相通信
client-to-client

# Uncomment this directive if multiple clients
# might connect with the same certificate/key
# files or common names.  This is recommended
# only for testing purposes.  For production use,
# each client should have its own certificate/key
# pair.
#
# IF YOU HAVE NOT GENERATED INDIVIDUAL
# CERTIFICATE/KEY PAIRS FOR EACH CLIENT,
# EACH HAVING ITS OWN UNIQUE "COMMON NAME",
# UNCOMMENT THIS LINE OUT.
;duplicate-cn

# The keepalive directive causes ping-like
# messages to be sent back and forth over
# the link so that each side knows when
# the other side has gone down.
# Ping every 10 seconds, assume that remote
# peer is down if no ping received during
# a 120 second time period.
keepalive 10 120

# For extra security beyond that provided
# by SSL/TLS, create an "HMAC firewall"
# to help block DoS attacks and UDP port flooding.
#
# Generate with:
#   openvpn --genkey tls-auth ta.key
#
# The server and each client must have
# a copy of this key.
# The second parameter should be '0'
# on the server and '1' on the clients.
# 如果没有 HMAC 防火墙的话,不需要这个东西
;tls-auth ta.key 0 # This file is secret

# Select a cryptographic cipher.
# This config item must be copied to
# the client config file as well.
# Note that v2.4 client/server will automatically
# negotiate AES-256-GCM in TLS mode.
# See also the ncp-cipher option in the manpage
# 就用默认的 CBC,有些客户端不支持 GCM,比如 RouterOS
cipher AES-256-CBC

# Enable compression on the VPN link and push the
# option to the client (v2.4+ only, for earlier
# versions see below)
# 启用数据压缩
compress lz4-v2
push "compress lz4-v2"

# For compression compatible with older clients use comp-lzo
# If you enable it here, you must also
# enable it in the client config file.
;comp-lzo

# The maximum number of concurrently connected
# clients we want to allow.
;max-clients 100

# It's a good idea to reduce the OpenVPN
# daemon's privileges after initialization.
#
# You can uncomment this out on
# non-Windows systems.
# 启动 openvpn daemon 后,降低进程的权限
user nobody
group nogroup

# The persist options will try to avoid
# accessing certain resources on restart
# that may no longer be accessible because
# of the privilege downgrade.
persist-key
persist-tun

# Output a short status file showing
# current connections, truncated
# and rewritten every minute.
status /var/log/openvpn/openvpn-status.log

# By default, log messages will go to the syslog (or
# on Windows, if running as a service, they will go to
# the "\Program Files\OpenVPN\log" directory).
# Use log or log-append to override this default.
# "log" will truncate the log file on OpenVPN startup,
# while "log-append" will append to it.  Use one
# or the other (but not both).
;log         /var/log/openvpn/openvpn.log
;log-append  /var/log/openvpn/openvpn.log

# Set the appropriate level of log
# file verbosity.
#
# 0 is silent, except for fatal errors
# 4 is reasonable for general usage
# 5 and 6 can help to debug connection problems
# 9 is extremely verbose
verb 3

# Silence repeating messages.  At most 20
# sequential messages of the same message
# category will be output to the log.
;mute 20

# Notify the client that when the server restarts so it
# can automatically reconnect.
# UDP only.
#explicit-exit-notify 1

8. 服务端 nftables 防火墙配置

只要把 OpenVPN 服务端配置文件中指定的端口打开即可。这里给一个通用的配置:

#!/usr/sbin/nft -f

# table
add table inet filter



# chain
add chain inet filter input { type filter hook input priority filter; policy drop; }
add chain inet filter my_input_tcp
add chain inet filter my_input_udp
add chain inet filter prerouting { type nat hook prerouting priority 100; policy accept; }
add chain inet filter forward { type filter hook forward priority filter; policy drop; }
add chain inet filter output { type filter hook output priority 0; policy drop; }
add chain inet filter postrouting { type nat hook postrouting priority 100; policy accept; }



# rule

# filter input rule
add rule inet filter input iif "lo" accept comment "Accept any localhost traffic"
add rule inet filter input ct state { established, related } counter accept comment "Accept traffic originated from us"
add rule inet filter input ct state invalid log prefix "Invalid-Input: " level info flags all counter drop comment "Drop invalid connections"
add rule inet filter input ip protocol icmp icmp type echo-request limit rate 10/second counter accept comment "No ping floods"
add rule inet filter input ip protocol icmp icmp type { destination-unreachable, router-advertisement, router-solicitation, time-exceeded, parameter-problem } accept comment "Accept ICMP"
add rule inet filter input ip protocol igmp accept comment "Accept IGMP"
add rule inet filter input meta l4proto tcp jump my_input_tcp
add rule inet filter input meta l4proto udp jump my_input_udp

# filter input > my_input_tcp rule
add rule inet filter my_input_tcp tcp dport 22 ct state new limit rate 5/minute log prefix "New SSH connection: " counter accept comment "Avoid brute force on SSH"
add rule inet filter my_input_tcp tcp dport { 80, 443 } counter packets 0 bytes 0 accept comment "Accept web traffic"
add rule inet filter my_input_tcp tcp dport { 39393 } counter packets 0 bytes 0 accept comment "Accept OpenVPN tcp traffic"
add rule inet filter my_input_tcp tcp dport { 53, 583 } counter packets 0 bytes 0 accept comment "Accept Adguardhome DNS tcp and DoT traffic"

# filter input > my_input_udp rule
add rule inet filter my_input_udp udp dport { 53 } counter packets 0 bytes 0 accept comment "Adguardhome DNS udp port"
add rule inet filter my_input_udp udp dport { 39393 } counter packets 0 bytes 0 accept comment "Accept OpenVPN udp traffic"



# filter forward rule
add rule inet filter forward ct state invalid drop
add rule inet filter forward ct state established,related accept



# filter output rule
add rule inet filter output ct state invalid drop
add rule inet filter output ct state new,established,related accept
add rule inet filter output ip6 nexthdr ipv6-icmp accept

9. OpenVPN 客户端配置

9.1. Linux 作为客户端

安装 OpenVPN sudo apt install -y openvpn 后,复制配置文件 cp /usr/share/doc/openvpn/examples/sample-config-files/client.conf /etc/openvpn/server/myclient.conf 到客户端配置目录下,改好配置后:

  • 让 OpenVPN 开机自启: sudo systemctl enable openvpn-server@myclient
  • 重新启动 OpenVPN: sudo systemctl restart openvpn-server@myclient

这里记录一个自用的配置。

/etc/openvpn/client/myclient.conf 中:

##############################################
# Sample client-side OpenVPN 2.0 config file #
# for connecting to multi-client server.     #
#                                            #
# This configuration can be used by multiple #
# clients, however each client should have   #
# its own cert and key files.                #
#                                            #
# On Windows, you might want to rename this  #
# file so it has a .ovpn extension           #
##############################################

# Specify that we are a client and that we
# will be pulling certain config file directives
# from the server.
client

# Use the same setting as you are using on
# the server.
# On most systems, the VPN will not function
# unless you partially or fully disable
# the firewall for the TUN/TAP interface.
;dev tap
dev tun0

# Windows needs the TAP-Win32 adapter name
# from the Network Connections panel
# if you have more than one.  On XP SP2,
# you may need to disable the firewall
# for the TAP adapter.
;dev-node MyTap

# Are we connecting to a TCP or
# UDP server?  Use the same setting as
# on the server.
;proto tcp
proto udp

# The hostname/IP and port of the server.
# You can have multiple remote entries
# to load balance between the servers.
remote <server_ip> 39393
;remote my-server-2 1194

# Choose a random host from the remote
# list for load-balancing.  Otherwise
# try hosts in the order specified.
;remote-random

# Keep trying indefinitely to resolve the
# host name of the OpenVPN server.  Very useful
# on machines which are not permanently connected
# to the internet such as laptops.
resolv-retry infinite

# Most clients don't need to bind to
# a specific local port number.
;nobind

# Downgrade privileges after initialization (non-Windows only)
user nobody
group nogroup

# Try to preserve some state across restarts.
persist-key
persist-tun

# If you are connecting through an
# HTTP proxy to reach the actual OpenVPN
# server, put the proxy server/IP and
# port number here.  See the man page
# if your proxy server requires
# authentication.
;http-proxy-retry # retry on connection failures
;http-proxy [proxy server] [proxy port #]

# Wireless networks often produce a lot
# of duplicate packets.  Set this flag
# to silence duplicate packet warnings.
;mute-replay-warnings

# SSL/TLS parms.
# See the server config file for more
# description.  It's best to use
# a separate .crt/.key file pair
# for each client.  A single ca
# file can be used for all clients.
ca ca.crt
cert openvpn-client-dy.crt
key openvpn-client-dy.key

# Verify server certificate by checking that the
# certificate has the correct key usage set.
# This is an important precaution to protect against
# a potential attack discussed here:
#  http://openvpn.net/howto.html#mitm
#
# To use this feature, you will need to generate
# your server certificates with the keyUsage set to
#   digitalSignature, keyEncipherment
# and the extendedKeyUsage to
#   serverAuth
# EasyRSA can do this for you.
;remote-cert-tls server

# If a tls-auth key is used on the server
# then every client must also have the key.
;tls-auth ta.key 1

# Select a cryptographic cipher.
# If the cipher option is used on the server
# then you must also specify it here.
# Note that v2.4 client/server will automatically
# negotiate AES-256-GCM in TLS mode.
# See also the data-ciphers option in the manpage
cipher AES-256-CBC

# Enable compression on the VPN link.
# Don't enable this unless it is also
# enabled in the server config file.
#comp-lzo

# Set log file verbosity.
verb 3

# Silence repeating messages
;mute 20

9.2. RouterOS 作为客户端

将生成的客户端 .key 私钥,客户端 .crt 证书和根证书 ca.crt 导入到 RouterOS 系统中。

然后在 interface 中添加一个 ovpn-out 即可。

10. 踩坑

10.1. 一个客户端会消耗服务端 4 个 IP

配置文件的注释写了如何给用某个证书连过来的客户端配固定 IP:

# EXAMPLE: Suppose you want to give
# Thelonious a fixed VPN IP address of 10.9.0.1.
# First uncomment out these lines:
;client-config-dir ccd
;route 10.9.0.0 255.255.255.252
# Then add this line to ccd/Thelonious:
#   ifconfig-push 10.9.0.1 10.9.0.2

注意到, ifconfig-push 段里除了要分配的 10.9.0.1 之外,还多出了一个 10.9.0.2

第一个 IP 给客户端进程管理的接口;第二个 IP 给服务端进程管理的接口 ( 作为服务端内部的虚拟 IP 之一 )。

在 OpenVPN 1.6 中,每个客户端都运行一个 OpenVPN 实例时,OpenVPN 的行为更像是在服务端和每个客户端之间使用 P2P 连接。

然而,在 2.0 中 ,OpenVPN 可以通过服务端上的一个 tun 接口处理多个客户端。而这个功能的实现方式是,让服务端所在的操作系统创建一个它自身到 OpenVPN 服务端的一个 ( 唯一的 ) P2P 连接,然后当客户端连接到 OpenVPN 服务端时再创建一个客户端到服务端所在操作系统的一个 P2P 连接。

即,OpenVPN 客户端进程 <-- P2P 连接到 --> 服务端操作系统 <-- P2P 连接到 --> OpenVPN 服务端进程。

如果所有的操作系统都支 tun 接口的这个特性,那么可以做到不划子网,服务端只使用一个 IP 地址 ( 这个 IP 地址就是由服务端进程管理的接口的 IP 地址 ),客户端可以被分配到连续的地址。

但是,由于 TAP-Win32 驱动程序的 TUN 仿真模式的限制,Windows 不能完全支持这个特性。所以,OpenVPN 采用为每个客户端划分一个 /30 ( 255.255.255.252 ) 子网的方式来兼容 Windows 上的 OpenVPN 客户端。而这个 /30 子网只有 4 个取值:

  • xxxx xxxx . xxxx xxxx . xxxx xxxx . xxxx xx00 网段地址,不能分配给设备。
  • xxxx xxxx . xxxx xxxx . xxxx xxxx . xxxx xx01 第一个可用的地址:
    • 如果 /30 子网给服务端使用,这个地址被分配给服务端,作为服务端进程管理的接口的真实 IP。
    • 如果 /30 子网给客户端使用,这个地址被分配给客户端,作为客户端进程管理的接口的 IP。
  • xxxx xxxx . xxxx xxxx . xxxx xxxx . xxxx xx10 第二个可用的地址:
    • 不论 /30 子网给客户端还是服务端使用,这个地址都被分配给服务端,作为服务端进程管理的接口的虚拟 IP 之一。
  • xxxx xxxx . xxxx xxxx . xxxx xxxx . xxxx xx11 广播地址,不能分配给设备。

( 如果管理员知道只有非 Windows 客户端才会连接到 OpenVPN 服务端,那么可以在服务端配置文件中加入 ifconfig-pool-linear 指令来避免这种划分子网的行为。这个行为只是为了兼容 Windows 上的客户端。)

如果在 /24 网段中再划分 /30 子网的话,客户端可用的网段只有 63 个: /24 网段总共有 256 个 IP 地址,4 个 IP 地址划一个子网,就得到 64 个子网;在把这 64 个子网分给客户端前,服务端得先用掉一个,只剩 63 个。

所以,在 OpenVPN 服务端启动时,会有一个 xxxx xxxx . xxxx xxxx . xxxx xxxx . xxxx xx01 到 xxxx xxxx . xxxx xxxx . xxxx xxxx . xxxx xx01 的 ( 比如 192.168.0.1 <-> 192.168.0.2 ),服务端进程到服务端操作系统的,P2P 连接。

OpenVPN 会为每个连过来的客户端分配一个 /30 的子网,子网中共包含 2 个可被分配给设备的 IP,一个被分到客户端进程管理的接口上,一个被分到服务端进程管理的接口上。举个例子,/24 网段中第一个可分配给客户端的 /30 子网是 192.168.0.4/30 网段 ( 注意 /24 网段中的第一个 /30 子网 192.168.0.0/30 已经被服务端用了,所以只能把第二个 /30 子网 192.168.0.4/30 分给客户端 ):

  • 192.168.0.4 网段地址。不能分配给设备。
  • 192.168.0.5 给客户端上,客户端进程管理的接口使用。
  • 192.168.0.6 给服务端上,服务端进程管理的接口使用,是服务端内部的虚拟 IP 之一。
  • 192.168.0.7 广播地址。不能分配给设备。

如果在服务端运行 tcpdump -nn -i tun0 抓包看一下的话:

  • 从客户端 ping 192.168.0.1,会看到 icmp 包正常流入服务端 tun0 接口,回包正常从 tun0 接口流出。
  • 从客户端 ping 192.168.0.2,会看到 icmp 包正常流入服务端 tun0 接口,无回包。
  • 假设客户端 IP 为 192.168.0.5,从客户端 ping 192.168.0.6,会看到 icmp 包正常流入服务端 tun0 接口,无回包。

由于 192.168.0.2 和 192.168.0.6 只是 OpenVPN 服务端内部的虚拟 IP 地址,作为路由的端点。OpenVPN 不需要回答这个地址上的 ping,而 192.168.0.1 是服务端的真实IP地址,因此它需要回复 ping 。

为了可以连接到 OpenVPN 客户端之后的网络,用户需要从服务端推送一个路由到客户端,以便客户端通过 192.168.0.6 传输流量到 OpenVPN 服务端。

这种为了兼容而做出的妥协确实会浪费一些 IP 地址,但这是允许在 OpenVPN 支持的所有操作系统上进行一致配置的最佳方式。

TAP-Win32 驱动程序包括一个 DHCP 服务器,它将 192.168.0.5 地址分配给客户端,这也就是客户端会将 192.168.1.6 识别为 DHCP 服务器地址的原因。

另外,根据 /30 子网的划分方法,可以知道 /24 网段可被用于分配的地址段只有:

如果服务端使用 192.168.0.0/24 网段,那么可被分配的地址只有 192.168.0.{
{1,2}     | {5,6}     | {9,10}    | {13,14}   | {17,18}   |
{21,22}   | {25,26}   | {29,30}   | {33,34}   | {37,38}   |
{41,42}   | {45,46}   | {49,50}   | {53,54}   | {57,58}   |
{61,62}   | {65,66}   | {69,70}   | {73,74}   | {77,78}   |
{81,82}   | {85,86}   | {89,90}   | {93,94}   | {97,98}   |
{101,102} | {105,106} | {109,110} | {113,114} | {117,118} |
{121,122} | {125,126} | {129,130} | {133,134} | {137,138} |
{141,142} | {145,146} | {149,150} | {153,154} | {157,158} |
{161,162} | {165,166} | {169,170} | {173,174} | {177,178} |
{181,182} | {185,186} | {189,190} | {201,202} | {205,206} |
{209,210} | {213,214} | {217,218} | {221,222} | {225,226} |
{229,230} | {233,234} | {237,238} | {241,242} | {245,246} |
{249,250} | {253,254}
}

其中,{1, 2} 都被服务端使用:
- 地址 1 是服务端进程管理的接口的真实 IP。
- 地址 2 是服务端进程管理的接口的虚拟 IP 之一。

剩下的 {} 中:
- 第一个地址被分配给客户端,作为客户端进程管理的接口的 IP。
- 第二个地址被分配给服务端,作为服务端进程管理的接口的虚拟 IP 之一。


Last Update: 2023-08-13 Sun 14:28

Generated by: Emacs 28.2 (Org mode 9.5.5)   Contact: [email protected]

若正文中无特殊说明,本站内容遵循: 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议